package com.ripservice.service;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ripservice.dto.LinkDTO;
import com.ripservice.dto.RouterDTO;
import com.ripservice.dto.TopologyDto;
import com.ripservice.entity.LinkEntity;
import com.ripservice.entity.RouterEntity;
import com.ripservice.entity.TopologyEntity;
import com.ripservice.mapper.TopologyMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 拓扑服务类，提供对网络拓扑结构的业务逻辑处理
 * 包括拓扑的增删改查功能
 */
@Service
@RequiredArgsConstructor
public class TopologyService {
    /**
     * 拓扑数据访问对象，用于与数据库交互
     */
    private final TopologyMapper topologyMapper;

    /**
     * JSON对象映射器，用于处理JSON数据的序列化和反序列化
     */
    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 在指定拓扑结构中添加一个新的路由器
     *
     * @param topologyId 拓扑结构ID
     * @param dto        添加路由器的数据传输对象
     * @return 添加成功的路由器实体
     */
    @Transactional
    public RouterEntity addRouter(Long topologyId, RouterDTO dto) {
        RouterEntity r = new RouterEntity();
        r.setTopologyId(topologyId);
        r.setRouterId(dto.getRouterId());
        r.setX(dto.getX());
        r.setY(dto.getY());
        r.setMetadata(dto.getMetadata());
        topologyMapper.insertRouter(r);
        r.setId(r.getId());
        topologyMapper.updateRouter(r);

        return r;
    }

    /**
     * 更新指定路由器的坐标位置
     *
     * @param router 包含路由器ID和新坐标信息的路由器实体
     */
    @Transactional
    public void updateRouterPosition(RouterEntity router) {
        // 验证路由器ID是否存在
        if (router.getId() == null) {
            throw new IllegalArgumentException("Router ID cannot be null");
        }
        
        // 更新路由器位置
        topologyMapper.updateRouter(router);
    }

    /**
     * 在指定拓扑结构中添加一条新的链路
     *
     * @param topologyId 拓扑结构ID
     * @param dto        添加链路的数据传输对象
     * @return 添加成功的链路实体
     */
    @Transactional
    public LinkEntity addLink(Long topologyId, LinkDTO dto) {
        LinkEntity l = new LinkEntity();
        l.setTopologyId(topologyId);
        l.setFromRouter(dto.getFrom());
        l.setToRouter(dto.getTo());
        l.setCost(dto.getCost());
        topologyMapper.insertLink(l);
        l.setId(l.getId());
        topologyMapper.updateLink(l);
        return l;
    }

    /**
     * 获取所有拓扑结构列表
     *
     * @return 拓扑实体列表
     */
    public List<TopologyEntity> listTopologies() {
        return topologyMapper.findAllTopologies();
    }

    /**
     * 根据ID获取指定的拓扑结构及其关联的路由器和链路信息
     *
     * @param id 拓扑结构ID
     * @return 对应的拓扑DTO对象，如果不存在则返回null
     */
    public TopologyDto getTopology(Long id) {
        // 根据ID查询拓扑结构实体
        TopologyEntity e = topologyMapper.findById(id);
        // 如果拓扑结构不存在，直接返回null
        if (e == null) return null;

        // 创建拓扑DTO对象，并设置基本信息
        TopologyDto dto = new TopologyDto();
        dto.setId(e.getId());
        dto.setName(e.getName());
        dto.setDescription(e.getDescription());

        // 设置路由器信息
        dto.setRouters(topologyMapper.findRoutersByTopology(id).stream().map(r -> {
            RouterDTO rDto = new RouterDTO();
            rDto.setRouterId(r.getRouterId());
            rDto.setX(r.getX());
            rDto.setY(r.getY());
            rDto.setMetadata(r.getMetadata());
            return rDto;
        }).collect(Collectors.toList()));

        // 设置链路信息
        dto.setLinks(topologyMapper.findLinksByTopology(id).stream().map(l -> {
            LinkDTO lDto = new LinkDTO();
            lDto.setFrom(l.getFromRouter());
            lDto.setTo(l.getToRouter());
            lDto.setCost(l.getCost());
            return lDto;
        }).collect(Collectors.toList()));

        return dto;
    }

    /**
     * 创建新的拓扑结构，并保存其关联的路由器和链路信息
     *
     * @param dto 拓扑数据传输对象
     * @return 新创建的拓扑实体
     */
    @Transactional
    public TopologyEntity createTopology(TopologyDto dto) {
        // 创建一个新的拓扑实体，并设置基本信息
        TopologyEntity e = new TopologyEntity();
        e.setName(dto.getName());
        e.setDescription(dto.getDescription());
        e.setCreatedAt(java.time.Instant.now());
        topologyMapper.insert(e);
        dto.setId(e.getId());
        topologyMapper.update(e);
        dto.setId(e.getId());
        for (RouterDTO r : dto.getRouters()) {
            RouterEntity rEntity = addRouter(e.getId(), r);
        }
        for (LinkDTO l : dto.getLinks()) {
            LinkEntity lEntity = addLink(e.getId(), l);
        }
        // 返回已创建的拓扑实体
        return e;
    }

    /**
     * 更新指定ID的拓扑结构信息，包括其关联的路由器和链路
     * 先删除原有的路由器和链路，再重新插入新的数据
     *
     * @param id  拓扑结构ID
     * @param dto 更新后的拓扑数据传输对象
     */
    @Transactional
    public void updateTopology(Long id, TopologyDto dto) {
        // 验证拓扑是否存在
        TopologyEntity existing = topologyMapper.findById(id);
        if (existing == null) {
            throw new IllegalArgumentException("Topology not found: " + id);
        }
        
        // 更新拓扑基本信息
        TopologyEntity entity = new TopologyEntity();
        entity.setId(id);
        entity.setName(dto.getName());
        entity.setDescription(dto.getDescription());
        entity.setCreatedAt(existing.getCreatedAt()); // 保留创建时间
        topologyMapper.update(entity);
        
        // 删除原有的路由器和链路
        topologyMapper.deleteRouters(id);
        topologyMapper.deleteLinks(id);
        
        // 重新插入新的路由器和链路
        for (RouterDTO r : dto.getRouters()) {
            addRouter(id, r);
        }
        for (LinkDTO l : dto.getLinks()) {
            addLink(id, l);
        }
    }

    /**
     * 删除指定ID的拓扑结构，包括其关联的路由器和链路
     * 
     * @param id 拓扑结构ID
     */
    public void deleteTopology(Long id) {
        // 调用服务层方法，删除指定ID的拓扑结构
        TopologyEntity t = topologyMapper.findById(id);
        topologyMapper.delete(id);
        topologyMapper.deleteRouters(id);
        topologyMapper.deleteLinks(id);

    }

    /**
     * 将指定ID的拓扑结构导出为JSON格式字符串
     * 
     * @param topologyId 拓扑结构ID
     * @return 拓扑结构的JSON字符串表示
     */
    public String exportTopologyToJson(Long topologyId) {
        // 根据ID查询拓扑结构实体
        TopologyEntity topology = topologyMapper.findById(topologyId);
        // 如果拓扑结构不存在，抛出异常
        if (topology == null) throw new IllegalArgumentException("topology not found: " + topologyId);

        // 查询该拓扑下的所有路由器和链路实体
        List<RouterEntity> routers = topologyMapper.findRoutersByTopology(topologyId);
        List<LinkEntity> links = topologyMapper.findLinksByTopology(topologyId);

        // 构建包含拓扑、路由器和链路信息的Map对象
        Map<String, Object> map = new LinkedHashMap<>();
        map.put("topology", topology);
        map.put("routers", routers);
        map.put("links", links);

        try {
            // 使用ObjectMapper将Map对象序列化为格式化的JSON字符串
            return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(map);
        } catch (IOException e) {
            // 序列化失败时抛出运行时异常
            throw new RuntimeException("Failed to serialize topology to JSON", e);
        }
    }

    /**
     * 从文件导入拓扑（返回新建 topology 实体，含生成的 id）
     * 
     * @param file 包含拓扑信息的JSON文件
     * @return 新创建的拓扑实体
     */
    @Transactional
    public TopologyEntity importTopologyFromFile(MultipartFile file) {
        try {
            // 检查文件是否为空
            if (file == null || file.isEmpty()) {
                throw new IllegalArgumentException("File is empty");
            }
            
            // 读取文件内容并解析为JSON节点树
            String json = new String(file.getBytes(), StandardCharsets.UTF_8);
            JsonNode root = objectMapper.readTree(json);

            // 获取拓扑节点并验证其存在性
            JsonNode topoNode = root.get("topology");
            if (topoNode == null || topoNode.isNull()) {
                throw new IllegalArgumentException("Invalid file: missing topology");
            }
            
            // 将拓扑节点转换为TopologyEntity对象
            TopologyEntity topology = objectMapper.treeToValue(topoNode, TopologyEntity.class);
            // 确保创建新的拓扑（清空ID）
            topology.setId(null);
            // 插入新拓扑并获取生成的ID
            topologyMapper.insert(topology);
            Long topologyId = topology.getId();

            // 处理路由器节点
            JsonNode routersNode = root.get("routers");
            if (routersNode != null && routersNode.isArray()) {
                for (JsonNode rnode : routersNode) {
                    // 将路由器节点转换为RouterEntity对象
                    RouterEntity r = objectMapper.treeToValue(rnode, RouterEntity.class);
                    // 验证路由器ID的存在性
                    if (!StringUtils.hasText(r.getRouterId())) {
                        throw new IllegalArgumentException("Router must have routerId");
                    }
                    // 清空原来的路由id
                    r.setId(null);
                    // 设置拓扑ID并插入路由器
                    r.setTopologyId(topologyId);
                    topologyMapper.insertRouter(r);
                }
            }

            // 处理链路节点
            JsonNode linksNode = root.get("links");
            if (linksNode != null && linksNode.isArray()) {
                for (JsonNode lnode : linksNode) {
                    // 将链路节点转换为LinkEntity对象
                    LinkEntity l = objectMapper.treeToValue(lnode, LinkEntity.class);
                    // 验证起始和目标路由器的存在性
                    if (!StringUtils.hasText(l.getFromRouter()) || !StringUtils.hasText(l.getToRouter())) {
                        throw new IllegalArgumentException("Link must have fromRouter and toRouter");
                    }
                    // 清空原来的链路id
                    l.setId(null);
                    // 设置拓扑ID并插入链路
                    l.setTopologyId(topologyId);
                    topologyMapper.insertLink(l);
                }
            }

            // 返回创建的拓扑实体
            return topology;
        } catch (IOException ex) {
            // 文件处理失败时抛出运行时异常
            throw new RuntimeException("Failed to import topology file", ex);
        }
    }
}